package com.peng.rpc.netty.server;

import com.peng.rpc.entity.RpcRequest;
import com.peng.rpc.entity.RpcResponse;
import com.peng.rpc.common.server.RequestHandler;
import com.peng.rpc.util.SingletonFactory;
import io.netty.channel.*;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.ReferenceCountUtil;
import lombok.extern.slf4j.Slf4j;


/**
 * @author Wonderson
 * @email wpp.ds@foxmail.com
 * @date 2022/3/1 21:40
 */
@Slf4j
public class NettyServerHandler extends SimpleChannelInboundHandler<RpcRequest> {

    private final RequestHandler requestHandler;

    /**
     多个NettyServerHandler实例都会获得同一个requestHandler对象
     */
    public NettyServerHandler() {
        requestHandler = SingletonFactory.getSingletonInstance(RequestHandler.class);
    }

    @Override
    /**
     * netty的SimpleInboundHandler可以自动处理T类型的流对象【转为该对象使用】
     */
    protected void channelRead0(ChannelHandlerContext ctx, RpcRequest rpcRequest) throws Exception {

        try {
            if (rpcRequest.getIsHeartBeat()) {
                log.info("收到客户端的心跳包");
                return;
            }
            log.info("服务器收到客户端的请求:{}", rpcRequest);
            Object result = requestHandler.handle(rpcRequest);
            Channel channel = ctx.channel();
            if (channel == null || !channel.isActive() || !channel.isWritable()) {
                log.error("通道：{}不可写", channel);
            } else {
                ctx.writeAndFlush(RpcResponse.success(result, rpcRequest.getRequestId()));
            }
        } finally {
            ReferenceCountUtil.release(rpcRequest);
        }

    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.error("处理请求时发生了错误：", cause);
        ctx.close();
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent) {
            IdleState state = ((IdleStateEvent) evt).state();
            //监听客户端写入的数据
            if (state == IdleState.READER_IDLE) {
                log.info("长时间未能监听到客户端的心跳包，开始断开连接...");
                ctx.close();
            }
        } else {
            super.userEventTriggered(ctx, evt);
        }
    }

}
